# 待拟合函数y=3x

import numpy as np
from copy import deepcopy

# 1.初始化数据
x = np.array([[1], [3], [4], [8], [10]])
y = np.array([[3], [9], [12], [24], [30]])

# 2.随机初始化权重和偏差值 y=ax+b  a、b
w = np.array([[[2.269]], [[1.01]]])


# 3.定义神经网络并计算平方误差损失值
def feed_forward(inputs, outputs, weights):
    pre_out = np.dot(inputs, weights[0]) + weights[1]
    squared_error = np.square(pre_out - outputs)
    return squared_error


# 4.更新权重
"""
    少量增加每个权重和偏置值，并针对每个权重和偏差更新一次计算一个平方误差损失值.
如果平方误差损失值随权重的增加而较小，则权重值应增加，权重值应增加的大小与权重变化减少的损失值的大小成正比。反之亦然。
另外，通过学习率确保增加的权重值小于因权重变化而导致的损失变化，这样可以确保损失值更平稳的减少
"""


def update_weights(inputs, outputs, weights, epochs=100):
    for epoch in range(epochs):
        # 原始损失
        org_loss = feed_forward(inputs, outputs, weights)
        # 确保对权重列表进行神复制，由于权重将在后续步骤中进行操作，深复制可剞劂由于子变量的更改而影响父变量的问题
        wts_tmp = deepcopy(weights)
        wts_tmp2 = deepcopy(weights)
        # 循环变量所有权重值，然后对其进行较小的更改 +0.0001
        for ix, wt in enumerate(weights):
            wts_tmp[ix] += 0.0001
            # 权重修改后，计算更新的前向传播损失。计算由于权重的微小变化而造成的损失变化
            # 由于我们要计算所有输入采样的均方误差，因此将损失的变化除以输入的数据数量
            loss = feed_forward(inputs, outputs, wts_tmp)
            del_loss = np.sum(org_loss - loss) / (0.0001 * len(inputs))
            # 通过损失变化来更新权重，通过将损失的变化乘以一个很小的数字(0.01)缓慢更新权重，这就是学习率参数
            wts_tmp2[ix] += del_loss * 0.01
            wts_tmp = deepcopy(weights)
        weights = deepcopy(wts_tmp2)
        print("第{}次训练，a:{} b:{}".format(epoch+1, weights[0], weights[1]))


update_weights(x, y, w, 1000)

""" 输出：
第998次训练，a:[[2.99933065]] b:[[0.00449773]]
第999次训练，a:[[2.99933359]] b:[[0.00447639]]
第1000次训练，a:[[2.99933652]] b:[[0.00445517]]
"""